package com.nulldev.util.fileformats.ftf;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.nulldev.util.crypto.encryptAPI.manage.EncryptAPI;
import com.nulldev.util.fileformats.ftf.FTFReader.EncryptionType;
import com.nulldev.util.io.FileUtil;
import com.nulldev.util.io.MD5Utils;
import com.nulldev.util.other.ArrayUtils;

/**
 * FTF, Formatted Text File <br>
 * Type anything you want and it works. <br>
 * Supported 'tags':<br>
 *   [image src=""] - Hooks an image from an URL <br>
 * 	 [image data=""] - Hooks an image from <b>Base 64</b> <br>
 *   [url src=""] - Hooks a hyperlink to the document. <br>
 *   [embed src=""] - Embeds an page from a url <br>
 * @author null
 *
 */
public class FTFDocument {

	public static final Pattern IMAGE = Pattern.compile("\\[image src=[\\s\\S]*?\"]");
	public static final Pattern URL = Pattern.compile("\\[url src=[\\s\\S]*?\"]");
	/**
	 * WARNING: Not supported via JEditorPane/JTextPane <br>
	 */
	public static final Pattern EMBED = Pattern.compile("\\[embed src=[\\s\\S]*?\"]");
	/**
	 * output [url data="*standard string of base64 image from html* [data:image/png;charset=utf-8;base64,...]"]
	 */
	public static final Pattern IMAGE_SRC = Pattern.compile("\\[image data=[\\s\\S]*?\"]");
	
	private HashMap<String, String> meta = new HashMap<String, String>();
	private String text;
	
	public FTFDocument(final String text) {
		this.text = text;
	}
	
	public void setMetadata(final HashMap<String, String> meta) {
		this.meta = meta;
	}
	
	public HashMap<String, String> cloneMetadata() {
		return (HashMap<String, String>) this.meta.clone();
	}
	
	/**
	 * Gets a entry from the metadata based on FTFMetadata
	 * @param entry
	 * @return String value of entry <b>OR</b> null if it doesn't exist.
	 */
	public String getMetadata(final FTFMetadata entry) {
		if (entry == null) {
			return null;
		}
		return this.meta.get(entry.getKey());
	}
	
	public String getMetadataEntry(final String key) {
		return this.meta.get(key);
	}
	
	public String toHTML() {
		final Matcher m = this.IMAGE.matcher(this.text);
		final Matcher m1 = this.URL.matcher(this.text);
		final Matcher m2 = this.IMAGE_SRC.matcher(this.text);
		final Matcher m3 = this.EMBED.matcher(this.text);
		String out = this.text.replaceAll("\n", "<br>");
		int mindex = 0, urlindex = 0, urlsindex = 0, embedindex = 0;
		while (true) {
			final boolean exists = m.find();
			if (!exists) {
				break;
			}else {
				final String u = m.group(mindex);
				final String url = u.substring(12, u.length() - 2);
				out = out.replaceAll(Pattern.quote(u), "<img src=\"" + url + "\"></img>");
				mindex += 1;
			}
		}
		while (true) {
			final boolean exists = m1.find();
			if (!exists) {
				break;
			}else {
				final String u = m1.group(urlindex);
				final String url = u.substring(10, u.length() - 2);
				out = out.replaceAll(Pattern.quote(u), "<a href=\"" + url + "\">Link</a>");
				urlindex += 1;
			}
		}
		while (true) {
			final boolean exists = m2.find();
			if (!exists) {
				break;
			}else {
				final String u = m2.group(urlsindex);
				final String url = u.substring(13, u.length() - 2);
				//System.out.println(url);
				out = out.replaceAll(Pattern.quote(u), "<img src=\"" + url + "\"></img>");
				urlsindex += 1;
			}
		}
		while (true) {
			final boolean exists = m3.find();
			if (!exists) {
				break;
			}else {
				final String u = m2.group(embedindex);
				final String url = u.substring(13, u.length() - 2);
				//System.out.println(url);
				out = out.replaceAll(Pattern.quote(u), "<iframe src=\"" + url + "\"></iframe>");
				embedindex += 1;
			}
		}
		return out;
	}
	
	public String getText() {
		return this.text;
	}
	
	public void setText(final String text) {
		this.text = text;
	}
	
	/**
	 * Writes FTF document to a file.
	 * @param file [File to be saved to, <b>SUFFIX MUST BE .ftf</b>]
	 * @param key [Key to encrypt with, can be null for EncryptionType.Compress, else will throw RuntimeException.]
	 * @param type [Encryption Type]
	 * @param includeKey [default=false]
	 * @param md [Addtional metadata to add]
	 * @throws Exception
	 */
	public void writeDocumentToFile(final File file, final String key,
			final EncryptionType type, final FTFMetadata... md) throws Exception {
		this.writeDocumentToFile(file, key, type, false, md);
	}
	
	/**
	 * Writes FTF document to a file.
	 * @param file [File to be saved to, <b>SUFFIX MUST BE .ftf</b>]
	 * @param key [Key to encrypt with, can be null for EncryptionType.Compress, else will throw RuntimeException.]
	 * @param type [Encryption Type]
	 * @param includeKey [Include key in file metadata]
	 * @param md [Addtional metadata to add]
	 * @throws Exception
	 */
	public void writeDocumentToFile(final File file, final String key, final EncryptionType type,
			final boolean includeKey, final FTFMetadata... md) throws Exception {
		if (type == null) {
			throw new RuntimeException("writeDocumentToFile() -> type == null!");
		}
		if (this.text == null) {
			throw new RuntimeException("writeDocumentToFile() -> text == null!");
		}
		if (file == null || file.isDirectory()) {
			throw new RuntimeException("writeDocumentToFile() -> file == null!");
		}
		if ((key == null || key.length() == 0) && includeKey) {
			if (!type.equals(EncryptionType.Compressed)) {
				throw new RuntimeException("writeDocumentToFile() -> key cannot be null for non Compressed encryption!");
			}
		}
		if (!file.exists()) {
			file.createNewFile();
		}
		System.out.println("key=" + key);
		System.out.println(this.text);
		final ArrayList<String> o = new ArrayList<String>();
		o.add("<FTF level=1><newline>");
		o.add("<metadata><newline>");
		o.add("encryptionType: " + type.getFileValue() + "<newline>");
		if (key != null && includeKey) {
			o.add("key: " + key + "<newline>");
		}
		final String enc = EncryptAPI.getEncryptorForEncryption(type.getEncryptorTag()).encrypt(this.text, key);
		final String hash = MD5Utils.getMD5Hex(enc);
		o.add("hash: " + hash + "<newline>");
		o.add("forceHashCheck: true<newline>");
		for (final FTFMetadata meta : md) {
			o.add(meta.getKey() + ": " + meta.getItem() + "<newline>");
		}
		o.add("</metadata><newline>");
		o.add("<data><newline>");
		o.add("fileData: " + enc + "<newline>");
		o.add("</data><newline>");
		FileUtil.writeFile(file, o);
	}
}
